﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JtextEpicsInterface;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Serialization;
using PSIEpics;
using jtext_EpicsClient;
using System.Collections;
using System.IO;
using System.Reflection;

namespace JEPy
{
    /// <summary>
    /// this is a plugin for JtextEpics, it allowed you to run a python script and proccess the pv data, 
    /// it uses the ironPython, so it supportc# object, this means you can use Device and IJERecord and etc.
    /// </summary>
    public class JePyPlugin:IJEPlugIn
    {
        const string MYNAME = "JePyPlugIn";
        const string CODENAME = "mycode";
        JECLient myClient;
        PythonExe MyPyExe;
        Hashtable RemoteTable;  //keep name
        Hashtable OutputTable;//     KEEP name
        Hashtable allTbale; ////keep pvalias OBJECT
        PVAliases AllAlias; //keep pvalias OBJECT
        string iniScript ;
        string RunScript;
        string PathAlias;
        bool isComplex;

        /// <summary>
        /// Gets the name of the plug in.
        /// <para>plaese use a const and hard code it </para>
        /// </summary>
        /// <value>
        /// The name of the plug in.read only
        /// </value>
        public string PlugInName
        {
            get
            {
                return MYNAME;
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="JePyPlugin"/> class.
        /// </summary>
        public JePyPlugin()
        {
            RemoteTable = new Hashtable();
            OutputTable = new Hashtable();
            allTbale = new Hashtable();
        }

        /// <summary>
        /// Inis the plug in.isComplex means it will pass complex object in ths python scope, means you can access IJEREcord by Alias_C, 
        /// ini.py is run onec before plugin initialized, you can declear variables&const here,run.py is the script runned every thime the record is flushed.
        /// alias.xml is a table indecates the pv you want to access, in case of the pv is not leggal as a python variable, an alias is asigned. these file is placed .\jepy\
        /// </summary>
        /// <param name="iniObject">The ini object.
        /// it is a string: JePyPlugIn;isComplex;ini.py;run.py;alias.xml</param>
        public void IniPlugIn(object iniObject, IJERecord record)
        {

            string StrConfig = (string)iniObject;//JePyPlugIn;isComplex;ini.py;run.py;alias.xml
            string[] StrConfigs = Regex.Split(StrConfig, ";", RegexOptions.IgnoreCase);
            int Config_index = StrConfig.IndexOf(MYNAME) + 1;
            isComplex =Convert.ToBoolean( StrConfigs[Config_index + 0]);
            iniScript = AppDomain.CurrentDomain.BaseDirectory.ToString() + "JePy//" +StrConfigs[Config_index + 1];
            RunScript = AppDomain.CurrentDomain.BaseDirectory.ToString() + "JePy//" +StrConfigs[Config_index + 2];
            PathAlias = AppDomain.CurrentDomain.BaseDirectory.ToString() + "JePy//" + StrConfigs[Config_index + 3];
            MyPyExe=new PythonExe();
            //LoadHelpperAssmebly("");
            //when there is non Local pv
            //epics
            iniAlias();
            if (RemoteTable.Count > 0)
            {
                myClient = new JECLient();
                foreach (string Pv in RemoteTable.Keys)
                {
                        //myClient.AddChannel<ExtTimeType<string>>(Pv.ToString());
                        myClient.AddChannel(Pv);
                }
            }
            MyPyExe.IniPyExe();
            MyPyExe.addCodeFromFile(RunScript, CODENAME);
            if (StrConfigs[Config_index + 1] != "null")
            {
                MyPyExe.ExcFromFile(iniScript);
                //try
                //{
                //    MyPyExe.ExcFromFile(iniScript);
                //    Console.WriteLine("Finsh runing Initialize Script");
                //}
                //catch
                //{
                //    Console.WriteLine("Initialize Script failed");
                //}
            }

            Console.WriteLine("JePy Initialized");
        }

        private void iniAlias()
        {
            AllAlias = PVAliases.Load(PathAlias);
            foreach (PVAlias tAlias in AllAlias.allPvs)
            {
                allTbale.Add(tAlias.PvName, tAlias);
                try 
                {
                    Console.WriteLine("alias added: " + tAlias.PvName + "; " + tAlias.Alias);
                }
                catch
                { }
                if (tAlias.isRmote == true)
                {
                    RemoteTable.Add(tAlias.PvName, tAlias.Alias);
                }
                if (tAlias.isOutput == true)
                {
                    OutputTable.Add(tAlias.PvName, tAlias.Alias);
                }
            }
            //ini epics for remote pv
        }


        /// <summary>
        /// Runs the plug in.
        /// <Para>the record will call this each time it get flushed,for input and out put, there  maybe differences.Look up example if you are making plugin</Para>
        /// the record who called this will have its ValB been changed(out put to ValB), others will have their PV been changes.
        /// </summary>
        /// <param name="myRecord">My record.The record that called the plugIn,you can set get and do anything</param>
        /// <returns>
        /// if successful
        /// </returns>
        public bool RunPlugIn(IJERecord myRecord)
        {
         
            Boolean fillOk= FillScope( myRecord);
            if (fillOk == false)
            {
                Console.WriteLine("fill scope error");
                return false;
            }
            //Console.WriteLine("Code runs");
            MyPyExe.ExeCode(CODENAME);
            OutPutPV(myRecord);

            return true;
        }

        /// <summary>
        /// before run script Fills the scope.
        /// </summary>
        private Boolean FillScope(IJERecord myRecord)
        {
            IJEServer tServer = myRecord.ParentServer;
            foreach (PVAlias tPV in allTbale.Values)
            {
                if (tPV.isDevice == true)
                {
                    try
                    {//add deviet to scope
                        MyPyExe.SetVarible(tPV.Alias,tServer.DeviceTable[tPV.PvName]);
                    }
                    catch
                    {
                        return false;
                    }
                    continue;
                }
                if (tPV.isRmote != true)
                {   //first ValA=ValB, so fill scope with ValB, and PlugIn cahnges the ValB
                    MyPyExe.SetVarible(tPV.Alias, tServer[tPV.PvName].VAL);
                    if (isComplex == true)
                    {
                        MyPyExe.SetVarible(tPV.Alias + "_C", tServer[tPV.PvName]);
                    }
                }
                else
                {
                    //for remote pv you can only acsses PVal
                    try
                    {
                        
                        MyPyExe.SetVarible(tPV.Alias, myClient.RecordVAL[tPV.PvName]);
                    }
                    catch 
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// after the script Output PV.
        /// </summary>
        private void OutPutPV(IJERecord myRecord)
        {
             IJEServer tServer = myRecord.ParentServer; 
            foreach (string tPVname in OutputTable.Keys)
            {
                if (((PVAlias)(allTbale[tPVname])).isDevice == true)
                {//normally wont happen.
                    continue;
                }
                object tVal=MyPyExe.GetVarible(((PVAlias)(allTbale[tPVname])).Alias);
                
                if (((PVAlias)(allTbale[tPVname])).isRmote != true)
                {   //first ValA=ValB, so fill scope with ValB, and PlugIn cahnges the ValB
 
                    if (tServer[tPVname].RecordProperty.DataClass==0)
                    {
                        
                        switch (tServer[tPVname].RecordProperty.DataType)
                        {
                            
                            case CommonConst.DT_DOUBLE:
                            {
                                if (myRecord.pvName == tPVname)
                                {
                                    tServer[tPVname].VAL = (double)tVal;
                                }
                                else
                                {
                                    tServer[tPVname].PVAL = (double)tVal;
                                }
                                break;
                            }
                            case CommonConst.DT_INT:
                            {

                                if (myRecord.pvName == tPVname)
                                {
                                    tServer[tPVname].VAL = (int)tVal;
                                }
                                else
                                {
                                    tServer[tPVname].PVAL = (int)tVal;
                                }
                                break;
                            }
                            case CommonConst.DT_SBYTE:
                            {
                                if (myRecord.pvName == tPVname)
                                {
                                    tServer[tPVname].VAL = (sbyte)tVal;
                                }
                                else
                                {
                                    tServer[tPVname].PVAL = (sbyte)tVal;
                                }
                                break;
                            }
                            case CommonConst.DT_INT16:
                            {
                                if (myRecord.pvName == tPVname)
                                {
                                    tServer[tPVname].VAL = (Int16)tVal;
                                }
                                else
                                {
                                    tServer[tPVname].PVAL = (Int16)tVal;
                                }
                                break;
                            }
                            case CommonConst.DT_INT64:
                            {
                                if (myRecord.pvName == tPVname)
                                {
                                    tServer[tPVname].VAL = (Int64)tVal;
                                }
                                else
                                {
                                    tServer[tPVname].PVAL = (Int64)tVal;
                                }
                                break;
                            }
                            default:
                            {
                                if (myRecord.pvName == tPVname)
                                {
                                    tServer[tPVname].VAL = (double)tVal;
                                }
                                else
                                {
                                    tServer[tPVname].PVAL = (double)tVal;
                                }
                                break;
                            }
                        }
                    }
                    else
                    {                        
                        switch (tServer[tPVname].RecordProperty.DataType)
                        {
                            case CommonConst.DT_DOUBLE:
                            {
                                if (myRecord.pvName == tPVname)
                                {
                                    tServer[tPVname].VAL = (double[])tVal;
                                }
                                else
                                {
                                    tServer[tPVname].PVAL = (double[])tVal;
                                }
                                break;
                            }
                            case CommonConst.DT_INT:
                            {
                                if (myRecord.pvName == tPVname)
                                {
                                    tServer[tPVname].VAL = (int[])tVal;
                                }
                                else
                                {
                                    tServer[tPVname].PVAL = (int[])tVal;
                                }
                                break;
                            }
                            case CommonConst.DT_SBYTE:
                            {
                                if (myRecord.pvName == tPVname)
                                {
                                    tServer[tPVname].VAL = (sbyte[])tVal;
                                }
                                else
                                {
                                    tServer[tPVname].PVAL = (sbyte[])tVal;
                                }
                                break;
                            }
                            case CommonConst.DT_INT16:
                            {
                                if (myRecord.pvName == tPVname)
                                {
                                    tServer[tPVname].VAL = (Int16[])tVal;
                                }
                                else
                                {
                                    tServer[tPVname].PVAL = (Int16[])tVal;
                                }
                                break;
                            }
                            case CommonConst.DT_INT64:
                            {
                                if (myRecord.pvName == tPVname)
                                {
                                    tServer[tPVname].VAL = (Int64[])tVal;
                                }
                                else
                                {
                                    tServer[tPVname].PVAL = (Int64[])tVal;
                                }
                                break;
                            }
                            default:
                            {
                                if (myRecord.pvName == tPVname)
                                {
                                    tServer[tPVname].VAL = (double[])tVal;
                                }
                                else
                                {
                                    tServer[tPVname].PVAL = (double[])tVal;
                                }
                                break;
                            }
                        }
                    }
                }
                else
                {
                    //for remote pv you can only acsses PVal
                    try
                    {
                        myClient.RecordVAL[tPVname] = tVal;
                    }
                    catch
                    { }
                }
            }
        }



        public void KillPlugIn()
        {
           
        }

        public void LoadHelpperAssmebly(string fileName)
        {
            //test code
            Assembly.LoadFrom(@"G:\project\vs2008\weibo\jtext_weibo\jtextioc\jepy\NetDimension.Weibo.dll");
            Console.WriteLine("WeiBo dll loaded"); 
        }


    }

    
    /// <summary>
    /// save pv and alias, since pv name may not be legal in python
    /// </summary>
    [Serializable]
    public class PVAlias
    {
        /// <summary>
        /// the PV naem
        /// </summary>
        public string PvName;
        /// <summary>
        /// alias for this pv name
        /// </summary>
        public string Alias;
        /// <summary>
        /// is this a device
        /// </summary>
        public bool isDevice;
        public bool isRmote;
        public bool isOutput;
        public PVAlias()
        {
        }
    }
    
    [XmlInclude(typeof(PVAlias))]
    [Serializable]
    public class PVAliases
    {
        public ArrayList allPvs;
        public void Save(string path)
        {
            XmlSerializer Xs=new XmlSerializer(typeof(PVAliases));
            Stream Fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
            Xs.Serialize(Fs, this);
            Fs.Close();
        }
        static public PVAliases Load(string path)
        {
            XmlSerializer Xs = new XmlSerializer(typeof(PVAliases));
            Stream Fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            PVAliases Aliases= (PVAliases)Xs.Deserialize(Fs);
            Fs.Close();
            return Aliases;
        }

        public PVAliases()
        {
            allPvs = new ArrayList();
        }

    }

}
